# coding=utf8
from pyjvm.classfile.reader import ClassBytesReader
from pyjvm.classfile.constpool import ConstantPool
from pyjvm.utils import *
from pyjvm.classfile.clstype import *
from typing import Optional, Callable


class LazyInitializer:

    def lazy_inited(self):
        if not self.__lazy_inited:
            self.lazy_init()
            self.__lazy_inited = True

    def __init__(self):
        self.__lazy_inited = False

    def lazy_init(self):
        pass


class LazyGet(object):

    def __init__(self, func: Callable):
        self.__f_get: Callable = func

    def __get__(self, instance, owner):
        instance.lazy_inited()
        return self.__f_get(instance)


class AttributeInfo(LazyInitializer):

    def __init__(self):
        LazyInitializer.__init__(self)
        self.cp: Optional[ConstantPool] = None
        self.name: Optional[str] = None
        self.attr_len: int = 0
        self.__lazy_code: bytes = b''

    @property
    def lazy_reader(self):
        return ClassBytesReader(self.__lazy_code)

    def pre_init(self, reader: ClassBytesReader, cp: ConstantPool):
        self.attr_len = reader.int32()
        self.__lazy_code = reader.read(self.attr_len)
        self.cp = cp

    def lazy_init(self):
        # 子类实现，reader = self.lazy_reader => do read logic
        pass

    @staticmethod
    def read_attribute(reader: ClassBytesReader, const_pool: ConstantPool):
        name = const_pool.read_utf8(reader)
        if name in UnParsedAttribute.TYPES:
            attr = UnParsedAttribute()
        else:
            attr = globals()[name + 'Attribute']()
        attr.pre_init(reader, const_pool)
        attr.name = name
        return attr

    @staticmethod
    def read_attributes(reader: ClassBytesReader, const_pool: ConstantPool) -> list:
        attributes_count = reader.int16()
        return [AttributeInfo.read_attribute(reader, const_pool)
                for _ in range(attributes_count)]


class UnParsedAttribute(AttributeInfo):
    TYPES = {'MethodParameters', 'RuntimeInvisibleAnnotations', 'RuntimeInvisibleParameterAnnotations',
             'RuntimeInvisibleTypeAnnotations', 'RuntimeVisibleAnnotations', 'RuntimeVisibleParameterAnnotations',
             'RuntimeVisibleTypeAnnotations', 'SourceDebugExtension', 'StackMapTable'}

    def __init__(self):
        AttributeInfo.__init__(self)
        self.code = None

    def pre_init(self, reader: ClassBytesReader, cp: ConstantPool):
        self.attr_len = reader.int32()
        self.code = reader.read(self.attr_len)


class ConstantValueAttribute(AttributeInfo):
    """
    表示一个常量的表达式。位于FieldInfo 结构的属性表。

    属性列表：
    name: 属性名称
    const_value：属性的值，
    """

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__val = None

    def lazy_init(self):
        const_idx: u2 = self.lazy_reader.u2()
        self.__val = self.cp.value_at_u2(const_idx)

    @LazyGet
    def const_value(self):
        return self.__val

    def __str__(self):
        return 'ConstantValueAttribute[%s]' % self.const_value


class ExceptionTableEntry(LazyInitializer):
    """
    异常处理器

    属性列表：
    start_pc 和 end_pc 代表异常处理器在 CodeAttribute code的有效范围
    他们必须为当前code 中某个jvm指令的有效索引。
    handler_pc 表示一个异常处理器的起点
    catch_type，如果为None，所有异常抛出都会调用这个异常处理器（可以用于实现finally语句）
                如果不为None，只有抛出的异常是指定的类或者其子类的实例时，才会调用异常处理器
    """

    def __init__(self):
        LazyInitializer.__init__(self)
        self.__start_pc: int = -1
        self.__end_pc: int = -1
        self.__handler_pc: int = -1
        self.__catch_type_index: int = -1
        self.__lazy_code = b''

    @staticmethod
    def new(reader: ClassBytesReader):
        self = ExceptionTableEntry()
        self.__lazy_code = reader.read(8)
        return self

    def lazy_init(self):
        reader = ClassBytesReader(self.__lazy_code)
        self.__start_pc = reader.uint16()
        self.__end_pc = reader.uint16()
        self.__handler_pc = reader.uint16()
        self.__catch_type_index = reader.uint16()

    @LazyGet
    def start_pc(self):
        return self.__start_pc

    @LazyGet
    def end_pc(self):
        return self.__end_pc

    @LazyGet
    def handler_pc(self):
        return self.__handler_pc

    @LazyGet
    def catch_type_index(self):
        return self.__catch_type_index

    @staticmethod
    def read_exception_table(reader: ClassBytesReader) -> list:
        table_len = reader.int16()
        return [ExceptionTableEntry.new(reader) for _ in range(table_len)]

    def __str__(self):
        return '''ExceptionTableEntry[start_pc: %d, end_pc: %d, handle_pc: %d
        catch_type_index: %s]''' % (
            self.start_pc, self.end_pc, self.handler_pc, self.catch_type_index)


class CodeAttribute(AttributeInfo):
    """
    位于MethodInfo 结构的属性中。
    Code 属性包含方法、实例初始化方法、类或接口初始化方法的jvm 指令和辅助信息。
    注意：native和abstract方法，MethodInfo结构属性不能有Code属性。

    属性列表：
    name：str 属性名称
    max_stack：int 当前方法的操作数栈在方法执行的最大深度
    max_locals：int 当前方法局部变量表的局部变量个数（包括传递的参数）
    注意：long和double类型的局部变量的最大索引是max_locals - 2
    code：bytes 当前方法的jvm指令的内容
    exception_table[]：表示code对应的异常处理器，成员为ExceptionTableEntry
    attributes[]：属性表，成员为AttributeInfo结构体。
                类型包括：LineNumberTable,
                          LocalVariableTypeTable,
                          LocalVariableTable,
                          StackMapTable
    """

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__max_stack = None
        self.__max_locals = None
        self.__code = None
        self.__exception_table = None
        self.__attributes = None
        self.__line_number_attr = None

    def lazy_init(self):
        reader = self.lazy_reader
        self.__max_stack = reader.uint16()
        self.__max_locals = reader.uint16()
        code_length = reader.int32()
        self.__code = reader.read(code_length)
        self.__exception_table = ExceptionTableEntry.read_exception_table(reader)
        self.__attributes = AttributeInfo.read_attributes(reader, self.cp)

    @LazyGet
    def max_stack(self):
        return self.__max_stack

    @LazyGet
    def max_locals(self):
        return self.__max_locals

    @LazyGet
    def code(self):
        return self.__code

    @LazyGet
    def exception_table(self):
        return self.__exception_table

    @LazyGet
    def attributes(self):
        return self.__attributes

    @LazyGet
    def line_number_attr(self):
        if self.__line_number_attr is None:
            for attr in self.attributes:
                if isinstance(attr, LineNumberTableAttribute):
                    self.__line_number_attr = attr
                    return attr
        return self.__line_number_attr

    def __str__(self):
        return '''CodeAttribute[max stack: %d,
            max_locals: %d,
            code: %s
            exception table: %s,
            attibutes : %s]''' % (self.max_stack, self.max_locals, self.code,
                                  iter_readable(self.exception_table),
                                  iter_readable(self.attributes))


class StackMapFrameType:
    SameFrame = (0, 63)  # 0-63
    SameLocals1StackItemFrame = (64, 246)
    SameLocals1StackItemFrameExtended = (247, 247)
    ChopFrame = (248, 250)
    SameFrameExtended = (251, 251)
    AppendFrame = (252, 254)
    FullFrame = (255, 255)

    Types = [SameFrame, SameLocals1StackItemFrame,
             SameLocals1StackItemFrameExtended, ChopFrame,
             SameFrameExtended, AppendFrame, FullFrame]

    @staticmethod
    def gettype(frame_type_val):
        for _type in StackMapFrameType.Types:
            type_min, type_max = _type
            if type_min <= frame_type_val <= type_max:
                return _type


class StackMapFrame:

    def __init__(self, frame_type_val):
        self.frame_type_val = frame_type_val

    @staticmethod
    def read_frame(reader: ClassBytesReader, const_pool: ConstantPool):
        frame_type_val = reader.uint8()
        frame_type = StackMapFrameType.gettype(frame_type_val)
        if frame_type == StackMapFrameType.SameFrame:
            return SameFrame(frame_type_val)
        elif frame_type == StackMapFrameType.SameLocals1StackItemFrame:
            return SameLocals1StackItemFrame(frame_type_val, reader, const_pool)
        elif frame_type == StackMapFrameType.SameLocals1StackItemFrameExtended:
            return SameLocals1StackItemFrameExtended(frame_type_val, reader, const_pool)
        elif frame_type == StackMapFrameType.ChopFrame:
            return ChopFrame(frame_type_val, reader)
        elif frame_type == StackMapFrameType.SameFrameExtended:
            return SameFrameExtended(frame_type_val, reader)
        elif frame_type == StackMapFrameType.AppendFrame:
            return AppendFrame(frame_type_val, reader, const_pool)
        return FullFrame(frame_type_val, reader, const_pool)

    @staticmethod
    def readframes(reader: ClassBytesReader, const_pool: ConstantPool):
        number_of_entries = reader.uint16()
        return [StackMapFrame.read_frame(reader, const_pool)
                for _ in range(number_of_entries)]


class SameFrame(StackMapFrame):
    def __init__(self, frame_type_val):
        StackMapFrame.__init__(self, frame_type_val)

    def __str__(self):
        return 'SameFrame[frame_type_val: %d]' % self.frame_type_val


class SameLocals1StackItemFrame(StackMapFrame):
    def __init__(self, frame_type_val, reader: ClassBytesReader, const_pool: ConstantPool):
        StackMapFrame.__init__(self, frame_type_val)
        self.stack = [VariableInfo(reader, const_pool)]

    def __str__(self):
        return '''SameLocals1StackItemFrame[frame_type_val: %d,
        stack: %s]''' % (self.frame_type_val, self.stack)


class SameLocals1StackItemFrameExtended(StackMapFrame):
    def __init__(self, frame_type_val, reader: ClassBytesReader, const_pool: ConstantPool):
        StackMapFrame.__init__(self, frame_type_val)
        self.offset_delta = reader.uint16()
        self.stack = [VariableInfo(reader, const_pool)]

    def __str__(self):
        return '''SameLocals1StackItemFrameExtended[frame_type_val: %d,
        offset delta: %d
        stack: %s]''' % (self.frame_type_val, self.offset_delta, self.stack)


class ChopFrame(StackMapFrame):
    def __init__(self, frame_type_val, reader: ClassBytesReader):
        StackMapFrame.__init__(self, frame_type_val)
        self.offset_delta = reader.uint16()

    def __str__(self):
        return '''ChopFrame[frame_type_val: %d,
        offset delta: %d]''' % (self.frame_type_val, self.offset_delta)


class SameFrameExtended(StackMapFrame):
    def __init__(self, frame_type_val, reader: ClassBytesReader):
        StackMapFrame.__init__(self, frame_type_val)
        self.offset_delta = reader.uint16()

    def __str__(self):
        return '''SameFrameExtended[frame_type_val: %d,
        offset delta: %d]''' % (self.frame_type_val, self.offset_delta)


class AppendFrame(StackMapFrame):

    def __init__(self, frame_type_val, reader: ClassBytesReader, cp: ConstantPool):
        StackMapFrame.__init__(self, frame_type_val)
        self.offset_delta = reader.uint16()
        frame_count = self.frame_type_val - 251
        self.locals = VariableInfo.read_vars(frame_count, reader, cp)

    def __str__(self):
        return '''AppendFrame[frame_type_val: %d,
        offset delta: %d,
        locals: %s]''' % (self.frame_type_val, self.offset_delta, iter_readable(self.locals))


class FullFrame(StackMapFrame):

    def __init__(self, frame_type_val, reader: ClassBytesReader, cp: ConstantPool):
        StackMapFrame.__init__(self, frame_type_val)
        self.offset_delta = reader.uint16()
        num_of_locals = reader.uint16()
        self.locals = VariableInfo.read_vars(num_of_locals, reader, cp)
        num_of_stack_items = reader.uint16()
        self.stack = VariableInfo.read_vars(
            num_of_stack_items, reader, cp)

    def __str__(self):
        return '''FullFrame[frame_type_val: %d,
        offset delta: %d,
        locals: %s,
        stack: %s]''' % (self.frame_type_val, self.offset_delta,
                         iter_readable(self.locals), iter_readable(self.stack))


class StackMapTableAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__entries = None

    def lazy_init(self):
        self.__entries = StackMapFrame.readframes(
            self.lazy_reader, self.cp)

    @LazyGet
    def entries(self):
        return self.__entries

    def __str__(self):
        return '''StackMapTableAttribute[entries:%s]''' % iter_readable(self.entries)


class VariableInfoTag:
    Top = 0
    Integer = 1
    Float = 2
    Null = 5
    UninitializedThis = 6
    Object = 7
    Uninitialized = 8
    Long = 4
    Double = 3


class VariableInfo:
    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        tag = reader.uint8()
        if tag == VariableInfoTag.Object:
            self.class_info = cp.read_info(reader)
        elif tag == VariableInfoTag.Uninitialized:
            self.offset = reader.uint16()
        self.tag = tag

    @staticmethod
    def read_vars(length, reader: ClassBytesReader, cp: ConstantPool):
        i = 0
        _vars = []
        while i < length:
            var_info = VariableInfo(reader, cp)
            if var_info.tag in (VariableInfoTag.Long, VariableInfoTag.Double):
                i += 2
            else:
                i += 1
            _vars.append(var_info)
        return _vars

    def __str__(self):
        return '''VariableInfo[tag:%s]''' % self.tag


class ExceptionsAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__table = None

    def lazy_init(self):
        self.__table = self.lazy_reader.int16s()

    @LazyGet
    def exception_index_table(self):
        return self.__table

    def __str__(self):
        return '''ExceptionsAttribute[exception_index_table:%s]''' % self.exception_index_table


class InnerClassAccessFlag:
    ACC_PUBLIC = 0x0001  # 声明为public 可以用外包访问
    ACC_PRIVATE = 0x0002  # 声明为private 该类内才能访问
    ACC_PROTECTED = 0x0004  # 声明为protected 子类才能访问
    ACC_STATIC = 0x0008  # 声明为static 静态成员
    ACC_FINAL = 0x0010  # 声明为final 对象构造好之后，就不能直接设置该字段
    ACC_INTERFACE = 0x0200  # 声明为interface
    ACC_ABSTRACT = 0x0400  # 声明为abstract
    ACC_SYNTHETIC = 0x1000  # synthetic，该类由编译器产生，不是由java代码生成的
    ACC_ANNOTATION = 0x2000  # 声明为annotation类型
    ACC_ENUM = 0x4000  # 声明为enum


class InnerClassInfo:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.inner_class_info = cp.read_info(reader)
        self.outer_class_info = cp.read_info(reader)
        self.name = cp.read_info(reader)
        self.inner_class_access_flags = reader.uint16()

    @staticmethod
    def read_inner_classes(reader: ClassBytesReader, cp: ConstantPool):
        number_of_classes = reader.uint16()
        return [InnerClassInfo(reader, cp) for _ in range(number_of_classes)]

    def __str__(self):
        return '''InnerClassInfo[
            inner_class_info:%s,
            outer_class_info:%s,
            name:%s,
            inner_class_access_flags:%d]''' % (self.inner_class_info,
                                               self.outer_class_info,
                                               self.name,
                                               self.inner_class_access_flags)


class InnerClassesAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__classes = None

    def lazy_init(self):
        self.__classes = InnerClassInfo.read_inner_classes(self.lazy_reader, self.cp)

    @LazyGet
    def classes(self):
        return self.__classes

    def __str__(self):
        return '''InnerClassesAttribute[classes: %s]''' % self.classes


class EnclosingMethodAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__class_info = None
        self.__method_info = None

    def lazy_init(self):
        cp = self.cp
        reader = self.lazy_reader
        self.__class_info = cp.read_info(reader)
        self.__method_info = cp.read_info(reader)

    @LazyGet
    def class_info(self):
        return self.__class_info

    @LazyGet
    def method_info(self):
        return self.__method_info

    def __str__(self):
        return '''EnclosingMethodAttribute[class_info: %s,
        method_info:%s]''' % (self.class_info, self.method_info)


class SyntheticAttribute(AttributeInfo):

    def __str__(self):
        return '''SyntheticAttribute'''


class SignatureAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__signature = None

    @LazyGet
    def signature(self):
        return self.__signature

    def lazy_init(self):
        self.__signature = self.cp.read_utf8(self.lazy_reader)

    def __str__(self):
        return '''Signature[signature: %s]''' % self.signature


class SourceFileAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__source_file = None

    @LazyGet
    def source_file(self):
        return self.__source_file

    def lazy_init(self):
        self.__source_file = self.cp.read_utf8(self.lazy_reader)

    def __str__(self):
        return '''SourceFileAttribute[source_file: %s]''' % self.source_file


class SourceDebugExtensionAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__debug_extension = None

    @LazyGet
    def debug_extension(self):
        return self.__debug_extension

    def lazy_init(self):
        self.__debug_extension = self.lazy_reader.utf8_of(self.attr_len)

    def __str__(self):
        return '''SourceDebugExtensionAttribute[debug_extension: %s]''' % self.debug_extension


class LineNumberTableEntry:

    def __init__(self, reader: ClassBytesReader):
        self.start_pc = reader.uint16()
        self.line_number = reader.uint16()

    def __str__(self):
        return '''LineNumberTableEntry[start_pc: %d,line_number: %d]''' % (
            self.start_pc, self.line_number)


class LineNumberTableAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__line_number_table = None

    def lazy_init(self):
        reader = self.lazy_reader
        self.__line_number_table = [LineNumberTableEntry(reader)
                                    for _ in range(reader.uint16())]

    @LazyGet
    def line_number_table(self):
        return self.__line_number_table

    def __str__(self):
        return '''LineNumberTableAttribute[line_number_table: %s]''' % (
            iter_readable(self.line_number_table))

    def get_line_number(self, pc: int) -> int:
        for entry in reversed(self.line_number_table):
            if pc >= entry.start_pc:
                return entry.line_number
        return -1


class LocalVariableTableEntry:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.start_pc = reader.uint16()
        self.length = reader.uint16()
        self.name = cp.read_utf8(reader)
        self.descriptor = cp.read_utf8(reader)
        self.info = cp.read_info(reader)

    def __str__(self):
        return '''LocalVariableTableEntry[start_pc: %d,
        length: %d,
        name: %s,
        descriptor: %s,
        info: %s]''' % (self.start_pc, self.length, self.name,
                        self.descriptor, self.info)


class LocalVariableTableAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__local_variable_table = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        self.__local_variable_table = [LocalVariableTableEntry(reader, cp)
                                       for _ in range(reader.uint16())]

    @LazyGet
    def local_variable_table(self):
        return self.__local_variable_table

    def __str__(self):
        return '''LocalVariableTableAttribute[local_variable_table: %s]''' % iter_readable(self.local_variable_table)


class LocalVariableTypeTableEntry:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.start_pc = reader.uint16()
        self.length = reader.uint16()
        self.name = cp.read_utf8(reader)
        self.signature = cp.read_utf8(reader)
        self.info = cp.read_info(reader)

    def __str__(self):
        return '''LocalVariableTypeTableEntry[start_pc: %s,
        length: %d,
        name: %s,
        signature: %s,
        info: %s]''' % (self.start_pc, self.length, self.name,
                        self.signature, self.info)


class LocalVariableTypeTableAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__local_variable_type_table = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        self.__local_variable_type_table = [LocalVariableTypeTableEntry(reader, cp)
                                            for _ in range(reader.int16())]

    @LazyGet
    def local_variable_type_table(self):
        return self.__local_variable_type_table

    def __str__(self):
        return '''LocalVariableTypeTableAttribute[local_variable_type_table: %s]''' % iter_readable(
            self.local_variable_type_table)


class DeprecatedAttribute(AttributeInfo):

    def __str__(self):
        return '''DeprecatedAttribute'''


class ElementValueTag:
    Byte = 'B'
    Char = 'C'
    Double = 'D'
    Float = 'F'
    Int = 'I'
    Long = 'J'
    Short = 'S'
    Boolean = 'Z'
    String = 's'
    Enum = 'e'
    Class = 'c'
    Annotaion = '@'
    Array = '['


CONST_VALUE_TAG_SET = set('BCDFIJSZs')


class ElementValue:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        tag = reader.char()
        if tag in CONST_VALUE_TAG_SET:
            self.value = cp.read_value(reader)
        elif tag == ElementValueTag.Enum:
            self.type_name = cp.read_utf8(reader)
            self.const_name = cp.read_utf8(reader)
        elif tag == ElementValueTag.Class:
            self.class_info = cp.read_utf8(reader)
        elif tag == ElementValueTag.Annotaion:
            self.annotation_value = Annotation(reader, cp)
        elif tag == ElementValueTag.Array:
            num_values = reader.uint16()
            self.values = [ElementValue(reader, cp) for _ in range(num_values)]
        self.tag = tag

    def __str__(self):
        return '''ElementValue[tag: %d]''' % self.tag


class ElementValuePair:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.name = cp.read_utf8(reader)
        self.value = ElementValue(reader, cp)

    def __str__(self):
        return '''ElementValuePair[name: %s, value: %s]''' % (self.name, self.value)


class Annotation:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.type = cp.read_utf8(reader)
        num_element_value_pairs = reader.uint16()
        self.element_value_pairs = [ElementValuePair(reader, cp)
                                    for _ in range(num_element_value_pairs)]

    def __str__(self):
        return '''Annotation[type: %s, 
        element_value_pairs: %s]''' % (self.type, self.element_value_pairs)


class RuntimeVisibleAnnotationsAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__annotations = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        self.__annotations = [Annotation(reader, cp)
                              for _ in range(reader.uint16())]

    @LazyGet
    def annotations(self):
        return self.__annotations

    def __str__(self):
        return '''%s[annotations:%s]''' % (self.__class__.__name__,
                                           self.annotations)


class RuntimeInvisibleAnnotationsAttribute(RuntimeVisibleAnnotationsAttribute):
    pass


class ParameterAnnotation:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        num_annotations = reader.uint16()
        self.annotations = [Annotation(reader, cp)
                            for _ in range(num_annotations)]

    def __str__(self):
        return '''%s[annotations:%s]''' % (self.__class__.__name__,
                                           self.annotations)


class RuntimeVisibleParameterAnnotationsAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__parameter_annotations = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        self.__parameter_annotations = [ParameterAnnotation(reader, cp)
                                        for _ in range(reader.uint16())]

    @LazyGet
    def parameter_annotations(self):
        return self.__parameter_annotations

    def __str__(self):
        return '''%s[parameter_annotations:%s]''' % (self.__class__.__name__,
                                                     self.parameter_annotations)


class RuntimeInvisibleParameterAnnotationsAttribute(RuntimeVisibleParameterAnnotationsAttribute):
    pass


class TypeParameterTarget:
    """
    本条注解添加在泛型类、泛型接口、泛型方法或者泛型构造器的第i个类型参数声明上
    """

    def __init__(self, reader: ClassBytesReader):
        self.type_parameter_index = reader.uint8()

    def __str__(self):
        return '''%s[type_parameter_index:%s]''' % (self.__class__.__name__,
                                                    self.type_parameter_index)


class SupertypeTarget:
    """
    本条注解添加在类声明或者接口声明的extends字句或implements子句的某个类型上面
    """

    def __init__(self, reader: ClassBytesReader):
        self.supertype_target = reader.uint8()

    def __str__(self):
        return '''%s[supertype_target:%s]''' % (self.__class__.__name__,
                                                self.supertype_target)


class TypeParameterBoundTarget:
    """
    本条注解添加在泛型类、泛型接口、泛型方法或者泛型构造器第j个类型参数声明中的第i个界限上面
    """

    def __init__(self, reader: ClassBytesReader):
        self.type_parameter_index = reader.uint8()
        self.bound_index = reader.uint8()

    def __str__(self):
        return '''%s[type_parameter_index:%s,
        bound_index:%s]''' % (self.__class__.__name__, self.type_parameter_index, self.bound_index)


class EmptyTarget:
    """
    本注解添加在字段声明、方法的返回值类型、新构造的对象的类型或是方法或构造器的接收者类型上面
    """
    pass

    def __str__(self):
        return '''%s''' % self.__class__.__name__


class FormalParameterTarget:
    """
    本注解添加在方法、构造器或者lambda表达式的形式参数声明中的类型上面
    """

    def __init__(self, reader: ClassBytesReader):
        self.formal_parameter_index = reader.uint8()

    def __str__(self):
        return '''%s[formal_parameter_index: %s]''' % (
            self.__class__.__name__, self.formal_parameter_index)


class ThrowsTarget:
    """
    本注解添加在方法声明或构造器声明的throws子句所提到的第i个类型上面
    """

    def __init__(self, reader: ClassBytesReader):
        self.throws_type_index = reader.uint16()

    def __str__(self):
        return '''%s[throws_type_index: %s]''' % (
            self.__class__.__name__, self.throws_type_index)


class LocalVarTargetTableEntry:

    def __init__(self, reader: ClassBytesReader):
        self.start_pc = reader.uint16()
        self.length = reader.uint16()
        self.index = reader.uint16()

    def __str__(self):
        return '''%s[start_pc: %s, length: %s, index: %s]''' % (
            self.__class__.__name__, self.start_pc, self.length, self.index)


class LocalVarTarget:
    """
    本注解添加在局部变量声明中的类型上面
    """

    def __init__(self, reader: ClassBytesReader):
        table_len = reader.uint16()
        self.table = [LocalVarTargetTableEntry(reader) for _ in range(table_len)]

    def __str__(self):
        return '''%s[table: %s]''' % (self.__class__.__name__, iter_readable(self.table))


class CatchTarget:
    """
    本注解添加在异常参数声明的第i个类型上面
    """

    def __init__(self, reader: ClassBytesReader):
        self.exception_table_index = reader.uint16()

    def __str__(self):
        return '''%s[exception_table_index: %s]''' % (self.__class__.__name__, self.exception_table_index)


class OffsetTarget:
    """
    本注解添加在instanceof表达式或new表达式类型上面，要么添加在方法引用表达式的::符号前方的类型上面
    """

    def __init__(self, reader: ClassBytesReader):
        self.offset = reader.uint16()

    def __str__(self):
        return '''%s[offset: %s]''' % (self.__class__.__name__, self.offset)


class TypeArgumentTarget:
    """
    本条注解要么添加在类型转换表达式的第i个类型上面，要么添加在显示类型参数列表的第i个类型参数上面。
    这种参数列表针对的是new表达式、显示构造器调用语句、方法调用表达式或者方法引用表达式。
    """

    def __init__(self, reader: ClassBytesReader):
        self.offset = reader.uint16()
        self.type_parameter_index = reader.uint8()

    def __str__(self):
        return '''%s[offset: %s, 
        type_parameter_index: %s]''' % (
            self.__class__.__name__, self.offset, self.type_parameter_index)


class TypePathEntry:

    def __init__(self, reader: ClassBytesReader):
        self.type_path_kind = reader.uint8()
        self.type_argument_index = reader.uint8()

    def __str__(self):
        return '''%s[type_path_kind: %s, 
        type_argument_index: %s]''' % (
            self.__class__.__name__, self.type_path_kind, self.type_argument_index)


class TypePath:

    def __init__(self, reader: ClassBytesReader):
        self.path_length = reader.uint8()
        self.path = [TypePathEntry(reader) for _ in range(self.path_length)]

    def __str__(self):
        return '''%s[path_length: %s, 
        path: %s]''' % (
            self.__class__.__name__, self.path_length, self.path)


class TypeAnnotation:
    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.target_info = TypeAnnotation.__read_target_info(reader)
        self.target_path = TypePath(reader)
        self.annotation = Annotation(reader, cp)

    def __str__(self):
        return '''%s[target_info: %s, 
        target_path: %s,
        annotation: %s]''' % (
            self.__class__.__name__, self.target_info, self.target_path, self.annotation)

    @staticmethod
    def __read_target_info(reader: ClassBytesReader):
        target_type = reader.uint8()
        if target_type in (0x00, 0x01):
            return TypeParameterTarget(reader)
        elif target_type == 0x10:
            return SupertypeTarget(reader)
        elif target_type in (0x11, 0x12):
            return TypeParameterBoundTarget(reader)
        elif target_type in (0x13, 0x14, 0x15):
            return EmptyTarget()
        elif target_type == 0x16:
            return FormalParameterTarget(reader)
        elif target_type == 0x17:
            return ThrowsTarget(reader)
        elif target_type in (0x40, 0x41):
            return LocalVarTarget(reader)
        elif target_type == 0x42:
            return CatchTarget(reader)
        elif 0x43 <= target_type <= 0x46:
            return OffsetTarget(reader)
        elif 0x47 <= target_type <= 0x4B:
            return TypeArgumentTarget(reader)


class RuntimeVisiableTypeAnnotationsAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__annotations = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        self.__annotations = [TypeAnnotation(reader, cp)
                              for _ in range(reader.uint16())]

    @LazyGet
    def annotations(self):
        return self.__annotations

    def __str__(self):
        return '''%s[
        annotations: %s]''' % (
            self.__class__.__name__, self.annotations)


class RuntimeInvisiableTypeAnnotationsAttribute(RuntimeVisiableTypeAnnotationsAttribute):
    pass


class AnnotationDefaultAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__default_val = None

    def lazy_init(self):
        self.__default_val = ElementValue(self.lazy_reader, self.cp)

    @property
    def default_value(self):
        return self.__default_val

    def __str__(self):
        return '''%s[
        default_value: %s]''' % (
            self.__class__.__name__, self.default_value)


class BootstrapMethod:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.bootstrap_method_ref = cp.read_info(
            reader)  # ConstantMethodHandleInfo
        self.bootstrap_arguments = reader.u2s()

    def __str__(self):
        return '''%s[bootstrap_method_ref: %s,
        bootstrap_arguments: %s]''' % (
            self.__class__.__name__, self.bootstrap_method_ref, self.bootstrap_arguments)


class BootstrapMethodsAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__bootstrap_methods = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        num_bootstrap_methods = reader.uint16()
        self.__bootstrap_methods = [BootstrapMethod(
            reader, cp) for _ in range(num_bootstrap_methods)]

    @LazyGet
    def bootstrap_methods(self):
        return self.__bootstrap_methods

    def __str__(self):
        return '''%s[bootstrap_methods: %s]''' % (
            self.__class__.__name__, self.bootstrap_methods)


class Parameter:

    def __init__(self, reader: ClassBytesReader, cp: ConstantPool):
        self.name = cp.read_info(reader)
        self.access_flags = reader.uint16()

    def __str__(self):
        return '''%s[name: %s, accass_flags:%s]''' % (
            self.__class__.__name__, self.name, self.access_flags)


class MethodParametersAttribute(AttributeInfo):

    def __init__(self):
        AttributeInfo.__init__(self)
        self.__parameters = None

    def lazy_init(self):
        reader = self.lazy_reader
        cp = self.cp
        parameters_count = reader.uint8()
        self.__parameters = [Parameter(reader, cp)
                             for _ in range(parameters_count)]

    @LazyGet
    def parameters(self):
        return self.__parameters

    def __str__(self):
        return '''%s[parameters: %s]''' % (
            self.__class__.__name__, self.parameters)


__all__ = ['AttributeInfo', 'AnnotationDefaultAttribute', 'BootstrapMethodsAttribute', 'CodeAttribute',
           'ConstantValueAttribute',
           'DeprecatedAttribute', 'ExceptionsAttribute', 'EnclosingMethodAttribute', 'InnerClassesAttribute',
           'LineNumberTableAttribute', 'LocalVariableTableAttribute', 'LocalVariableTypeTableAttribute',
           'MethodParametersAttribute', 'RuntimeVisibleParameterAnnotationsAttribute',
           'RuntimeVisibleAnnotationsAttribute', 'RuntimeVisiableTypeAnnotationsAttribute',
           'RuntimeInvisiableTypeAnnotationsAttribute', 'RuntimeInvisibleAnnotationsAttribute',
           'RuntimeInvisibleParameterAnnotationsAttribute', 'SourceFileAttribute',
           'StackMapTableAttribute', 'SignatureAttribute', 'SourceDebugExtensionAttribute',
           'SyntheticAttribute', 'UnParsedAttribute',
           'ExceptionTableEntry']
